from spConstants import *
from time import sleep, clock


########################################################################
class SmallProtocol_framer:
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        self.__buffer = bytearray()
        self.__header = {}
        self.__hsize  = 0x01        

    def __calcCRC(self, buffer):
        bcc = 0
        for i in buffer:
            bcc = bcc + i
        bcc = bcc % 256	
        return bcc

    def __CRC_Check(self, buffer):
        """
        """
        ret = False
        if(len(buffer)==1):
            if(buffer[0]==ACK or buffer[0]==NAK):
                ret = True
            
        else:
            bcc = self.__calcCRC(buffer[:len(buffer)-1])
            ret = bcc == buffer[len(buffer)-1]
        
        return ret
    
    def buildPacket(self, buffer, protocol=False):

        """
          Input:		buffer, protocol
          Output:		---
          Description:    Sends data to display, adding protocol:
                          DC1/DC2 | length | data..... | bcc
                          <DC1> = 17(dec.) = 0x11 <DC2> = 18(dec.) = 0x12 
                          length = count of user data (without <DC1/DC2>, without checksum bcc)
                          bcc = 1 byte = sum of all bytes incl. <DC1/DC2> and len, modulo 256
                          sends array, using uart_send(), lenght = data amount to be send
                          protocol = False (Command/Data to the display)
                          protocol = True (protocoll commands to the display)
          """

        lenbuf = len(buffer)			#length without DC and bcc	
        if(protocol):                           # protocol commands to display
            buffer.insert(0, 0x12)		#send DC2				
        else:					#Command/Data to the display
            buffer.insert(0, 0x11)		#send DC1

        buffer.insert(1, lenbuf)                #send data length

        bcc = self.__calcCRC(buffer)
        buffer.append(bcc)
        return buffer    
    
    
    def decode(self):
        ''' 
        Get the next frame from the buffer

        :returns: The frame data or ''
        '''
        try:
            if(self.__header['len']==1):
                start = 0
                end = 1     
            else:
                start  = 2
                end = self.__header['len']+2
                
            buffer = self.__buffer[start:end]
            return buffer     
        except:
            return None
    
    def advanceFrame(self):
        ''' Skip over the current framed message
        This allows us to skip over the current message after we have processed
        it or determined that it contains an error. It also has to reset the
        current frame header handle
        '''
        if(self.__header['len']==1):
            self.__buffer = self.__buffer[self.__header['len']:]
        else:
            self.__buffer = self.__buffer[self.__header['len']+3:]
        self.__header = {}   
        
    def isFrameReady(self):
        ''' Check if we should continue decode logic
        This is meant to be used in a while loop in the decoding phase to let
        the decoder know that there is still data in the buffer.
        
        :returns: True if ready, False otherwise
        '''
        return len(self.__buffer) >= self.__hsize
     
    def checkFrame(self):
        '''
        Check if the next frame is available. Return True if we were
        successful.
        '''
        try:
            self.populateHeader()
            frame_size = self.__header['len']
            if(frame_size==1):
                data = self.__buffer[:frame_size]
            else:
                data = self.__buffer[:frame_size+3]
            return self.__CRC_Check(data)
        except (IndexError, KeyError):
            return False       
    
    def populateHeader(self):
        ''' Try to set the headers `uid`, `len` and `crc`.
        
        This method examines `self.__buffer` and writes meta
        information into `self.__header`. It calculates only the
        values for headers that are not already in the dictionary.
        
        Beware that this method will raise an IndexError if
        `self.__buffer` is not yet long enough.
        '''
        if(len(self.__buffer)>1):
            if((self.__buffer[0]==ACK) and (self.__buffer[1]==DC1)) or ((self.__buffer[0]==ACK) and (self.__buffer[1]==DC2)):
                # discard ACK
                self.__buffer = self.__buffer[1:]
                self.__header['head'] = self.__buffer[0]
                size = self.__buffer[1]
                self.__header['len'] = size
                self.__header['crc'] = self.__buffer[size+2]                
            else:
                
                self.__buffer = self.__buffer[len(self.__buffer)+1:]
                print "what to do when in here?"
                pass
        else:
            self.__header['head'] = self.__buffer[0]
            self.__header['len'] = 1
            self.__header['crc'] = 0
        
    
    
    def addToFrame(self, message):
        '''
        This should be used before the decoding while loop to add the received
        data to the buffer handle.
    
        :param message: The most recent packet
        '''
        self.__buffer.extend(message)    
    
    def processIncomingPacket(self, data, callback):
        #if(len(data)==1):  # single ACK or NAK
            #callback(data)
        #else:
            ##todo: check result
            ##extract ack
            ##checksum
            ##callback(data) without frame.
            #print data
        
        ''' The new packet processing pattern
        
        This takes in a new request packet, adds it to the current
        packet stream, and performs framing on it. That is, checks
        for complete messages, and once found, will process all that
        exist.  This handles the case when we read N + 1 or 1 / N
        messages at a time instead of 1.
        
        The processed and decoded messages are pushed to the callback
        function to process and send.
        
        :param data: The new packet data
        :param callback: The function to send results to
                '''
        self.addToFrame(data)
        while self.isFrameReady():
            if self.checkFrame():
                result = self.decode()
                if result is None:
                    print "result is none"
                    raise Exception("Unable to decode response")
                
                self.advanceFrame()
                callback(result) # defer or push to a thread?
            else: 
                print "check frame failed"
                result = None
                callback(result)
                break

           

    def  send_Command(self, buffer, data=False, protocol=False):
        """
        sends buffer to display, takes care of the max length of bytes 
        that can be sent to the display.
        """
        #move this to send command of eDIP43A_Display calls several times the execute method
        if(data): # we are looking to receive data from the display o
            if(len(buffer)>255): # only one request at any time
                ack = NAK
                receivedbuffer = None
                return ack, receivedbuffer
            
            self.__send(buffer, protocol)
            ack, receivedbuffer = self.__receive_decode(data);        
            if(ack and len(receivedbuffer)>3):
                if(not self.__CRC_Check(receivedbuffer)):
                    ack = False
                else:
                    receivedbuffer = receivedbuffer[2:len(receivedbuffer)-1]
                    
            return ack, receivedbuffer                     
            
            
        while(len(buffer)!=0):
            
            #send the first 255 bytes
            if(len(buffer)>255):
                newbuffer = bytearray(buffer[0:255])
                buffer = buffer[255:]
            else:
                newbuffer = bytearray(buffer)
                buffer = bytearray()
                
            tries = 0
            while(tries<3):
                self.__send(newbuffer, protocol)
                ack, receivedbuffer = self.__receive_decode(data);
                if(ack):
                    sleep(1/100)
                    break;
                else:
                    sleep(1/100)
                    tries+=1
            
        return ack, receivedbuffer         

    